﻿// <copyright file="KinectSlidingTrigger.cs" company="Ucaya">
// </copyright>
// <author>Ucaya</author>

namespace KinectExtensions.Interactivity
{
    #region

    using System;
    using System.ComponentModel.Composition;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Media;
    using System.Windows.Media.Animation;

    using KinectExtensions.Helpers;
    using KinectExtensions.Nui;
    using KinectExtensions.Services;

    using Microsoft.Research.Kinect.Nui;

    #endregion

    public class KinectSlidingTrigger : KinectOverTrigger
    {
        #region Constants and Fields

        /// <summary>
        ///   FadeOnEnter depedency property.
        /// </summary>
        public static readonly DependencyProperty FadeOnEnterProperty = DependencyProperty.Register("FadeOnEnter", typeof(bool), typeof(KinectSlidingTrigger), new PropertyMetadata(true));

        /// <summary>
        ///   IsRightHand depedency property.
        /// </summary>
        public static readonly DependencyProperty IsRightHandProperty = DependencyProperty.Register("IsRightHand", typeof(bool), typeof(KinectSlidingTrigger), new PropertyMetadata((d, e) => ((KinectSlidingTrigger)d).OnIsRightHandChanged((bool)e.OldValue, (bool)e.NewValue)));

        /// <summary>
        ///   Orientation depedency property.
        /// </summary>
        public static readonly DependencyProperty OrientationProperty = DependencyProperty.Register("Orientation", typeof(Orientation), typeof(KinectSlidingTrigger), null);

        /// <summary>
        ///   ReversePosition depedency property.
        /// </summary>
        public static readonly DependencyProperty ReversePositionProperty = DependencyProperty.Register("ReversePosition", typeof(bool), typeof(KinectSlidingTrigger), null);

        /// <summary>
        ///   SliderDataTemplate depedency property.
        /// </summary>
        public static readonly DependencyProperty SliderDataTemplateProperty = DependencyProperty.Register("SliderDataTemplate", typeof(DataTemplate), typeof(KinectSlidingTrigger), new PropertyMetadata((d, e) => ((KinectSlidingTrigger)d).OnSliderDataTemplateChanged((DataTemplate)e.OldValue, (DataTemplate)e.NewValue)));

        private ContentControl contentControl;

        private bool hitTestOverContentControl;

        private bool lastHitTestResult;

        private HandPosition lastMaster;

        private MagneticKinectZone lastZone;

        #endregion

        #region Public Properties

        public bool FadeOnEnter
        {
            get
            {
                return (bool)this.GetValue(FadeOnEnterProperty);
            }

            set
            {
                this.SetValue(FadeOnEnterProperty, value);
            }
        }

        /// <summary>
        ///   Gets or sets HandTrackingService.
        /// </summary>
        [Import]
        public HandTrackingService HandTrackingService { get; set; }

        public bool IsRightHand
        {
            get
            {
                return (bool)this.GetValue(IsRightHandProperty);
            }

            set
            {
                this.SetValue(IsRightHandProperty, value);
            }
        }

        public Orientation Orientation
        {
            get
            {
                return (Orientation)this.GetValue(OrientationProperty);
            }

            set
            {
                this.SetValue(OrientationProperty, value);
            }
        }

        [Import]
        public OverlayService OverlayService { get; private set; }

        public bool ReversePosition
        {
            get
            {
                return (bool)this.GetValue(ReversePositionProperty);
            }

            set
            {
                this.SetValue(ReversePositionProperty, value);
            }
        }

        public DataTemplate SliderDataTemplate
        {
            get
            {
                return (DataTemplate)this.GetValue(SliderDataTemplateProperty);
            }

            set
            {
                this.SetValue(SliderDataTemplateProperty, value);
            }
        }

        #endregion

        #region Methods

        protected override void HitTestAssodiatedObject(out bool isOver, out Point overPosition)
        {
            this.hitTestOverContentControl = false;
            if (this.AssociatedObject != null)
            {
                HitResult hitResult;
                if (this.contentControl != null)
                {
                    hitResult = this.HitTestService.HitTestHandMaster(this.contentControl);
                    this.hitTestOverContentControl = true;
                }
                else
                {
                    hitResult = this.HitTestService.HitTestHandMaster(this.AssociatedObject);
                }

                isOver = hitResult.Result;
                overPosition = new Point(hitResult.RelativeTouchPosition.X, hitResult.RelativeTouchPosition.Y);
            }
            else
            {
                isOver = false;
                overPosition = new Point();
            }
            this.lastHitTestResult = isOver;
        }

        protected override void OnIsKinectOverChanged(bool oldValue, bool newValue)
        {
            base.OnIsKinectOverChanged(oldValue, newValue);
            this.SetSlidingAdorner();
            this.SetKinectZone();
            this.SetAssociatedElementState();
        }

        protected override void OnOverPositionChanged(Point oldValue, Point newValue)
        {
            base.OnOverPositionChanged(oldValue, newValue);

            if (!this.hitTestOverContentControl || !this.lastHitTestResult)
            {
                return;
            }

            if (this.Orientation == Orientation.Horizontal)
            {
                double x = this.ReversePosition ? 1 - newValue.X : newValue.X;

                if (x >= 1)
                {
                    this.IsKinectOver = false;
                    this.InvokeActions(null);
                }
            }

            if (this.Orientation == Orientation.Vertical)
            {
                double y = this.ReversePosition ? 1 - newValue.Y : newValue.Y;
                if (y >= 1)
                {
                    this.IsKinectOver = false;
                    this.InvokeActions(null);
                }
            }
        }

        /// <summary>
        ///   handles the IsRightHandProperty changes.
        /// </summary>
        /// <param name = "oldValue">The old value.</param>
        /// <param name = "newValue">The new value.</param>
        private void OnIsRightHandChanged(bool oldValue, bool newValue)
        {
        }

        /// <summary>
        ///   handles the SliderDataTemplateProperty changes.
        /// </summary>
        /// <param name = "oldValue">The old value.</param>
        /// <param name = "newValue">The new value.</param>
        private void OnSliderDataTemplateChanged(DataTemplate oldValue, DataTemplate newValue)
        {
        }

        private void SetAssociatedElementState()
        {
            if (this.FadeOnEnter)
            {
                if (this.IsKinectOver)
                {
                    var sb = new Storyboard();
                    var da = new DoubleAnimation();
                    da.To = 0;
                    Storyboard.SetTarget(da, this.AssociatedObject);
                    Storyboard.SetTargetProperty(da, new PropertyPath("Opacity"));
                    da.Duration = TimeSpan.FromMilliseconds(300);
                    da.EasingFunction = new SineEase();
                    sb.Children.Add(da);
                    sb.Begin();
                }
                else
                {
                    var sb = new Storyboard();
                    var da = new DoubleAnimation();
                    da.To = 1;
                    Storyboard.SetTarget(da, this.AssociatedObject);
                    Storyboard.SetTargetProperty(da, new PropertyPath("Opacity"));
                    da.Duration = TimeSpan.FromMilliseconds(300);
                    da.EasingFunction = new SineEase();
                    sb.Children.Add(da);
                    sb.Begin();
                }
            }
        }

        private void SetKinectZone()
        {
            HandPosition master = this.HandTrackingService.HandMaster;
            if (master == null)
            {
                return;
            }

            this.IsRightHand = master.JointId == JointID.HandRight;

            if (this.lastMaster != null)
            {
                if (this.lastZone == this.lastMaster.KinectZone)
                {
                    this.lastMaster.KinectZone = null;
                }
            }

            if (this.lastZone != null)
            {
                Action a = () => this.lastZone.Leave();
                a.Delay(100);
            }

            this.lastMaster = master;
            if (this.IsKinectOver)
            {
                Window h = Application.Current.MainWindow;
                if (h == null)
                {
                    return;
                }

                this.lastZone = new MagneticKinectZone();
                this.lastZone.HideOnCapture = true;

                Point center = this.AssociatedObject.TransformToVisual(h).Transform(new Point(this.AssociatedObject.ActualWidth / 2, this.AssociatedObject.ActualHeight / 2));
                this.lastZone.MagneticCenter = center;

                this.lastZone.Enter();

                this.lastMaster.KinectZone = this.lastZone;
            }
        }

        private void SetSlidingAdorner()
        {
            if (this.IsKinectOver)
            {
                Panel layer = null;
                if (this.OverlayService == null || this.OverlayService.OverlayProvider == null || (layer = this.OverlayService.OverlayProvider.GetOverlayLayer()) == null)
                {
                    return;
                }

                try
                {
                    this.contentControl = new ContentControl { Content = this, ContentTemplate = this.SliderDataTemplate };
                    GeneralTransform transform = this.AssociatedObject.TransformToVisual(layer);
                    Rect bounds = CustomVisualTreeHelper.GetHitableDescendantBounds(this.AssociatedObject);
                    bounds = transform.TransformBounds(bounds);
                    this.contentControl.Width = bounds.Width;
                    this.contentControl.Height = bounds.Height;
                    this.contentControl.RenderTransform = new TranslateTransform { X = bounds.X, Y = bounds.Y };
                    layer.Children.Add(this.contentControl);
                }
                catch
                {
                }
            }
            else
            {
                if (this.contentControl.Parent != null)
                {
                    ((Panel)this.contentControl.Parent).Children.Remove(this.contentControl);
                }

                this.contentControl = null;
            }
        }

        #endregion
    }
}